#include "esp_ota_ops.h"
#include "esp_smartconfig.h"
#include "lwip/ip4_addr.h"
#include "nvs.h"
#include "wifi.h"
#include "config.h"
#include "dhcpserver/dhcpserver.h"
#include "udp.h"

#include "esp_log.h"

#define TAG "wifi"
#define log_e(...) ESP_LOGE(TAG, __VA_ARGS__)
#define log_i(...) ESP_LOGI(TAG, __VA_ARGS__)
#define log_w(...) ESP_LOGW(TAG, __VA_ARGS__)

#ifdef CONFIG_SPIRAM
#define _malloc_(size) heap_caps_malloc(size, MALLOC_CAP_SPIRAM)
#else
#define _malloc_(size) malloc(size)
#endif

#define WIFI_MAGIC_START 0x10090126
#define WIFI_MAGIC_END 0x93690126

#define ESP_MAXIMUM_RETRY 5

const char *const WifiStatusType2Str[] = {
    "WIFI_STATUS_INIT",
    "WIFI_STATUS_DEINIT",
    "WIFI_STATUS_START",
    "WIFI_STATUS_STOP",
    "WIFI_STATUS_STA_START",
    "WIFI_STATUS_STA_STOP",
    "WIFI_STATUS_STA_CONNECTING",
    "WIFI_STATUS_STA_CONNECTED",
    "WIFI_STATUS_STA_DISCONNECTED",
    "WIFI_STATUS_AP_CONNECTED",
    "WIFI_STATUS_AP_DISCONNECTED",
    "WIFI_STATUS_SCAN_DONE",
    "WIFI_STATUS_GOT_IP",
    "WIFI_STATUS_LOST_IP",
    "WIFI_STATUS_AP_GOT_IP",
    "WIFI_STATUS_STA_CONNECT_RESULT",
    "WIFI_STATUS_STA_SSID",
    "Undefined",
};

wifi_stack_t wifi_stack = 0;

typedef void (*scan_done_cb_t)(void *data, uint16_t len);

void wifi_stop_sta();

bool wifi_overflow_check()
{
    if (wifi_stack)
    {
        if (wifi_stack->MASGIC_START != WIFI_MAGIC_START)
        {
            log_e("!!! WiFi stack Overflow !!!");
            return true;
        }
        if (wifi_stack->STORAGE_MASGIC_START != WIFI_MAGIC_START)
        {
            log_e("!!! WiFi Storage stack Overflow !!!");
            return true;
        }
        if (wifi_stack->STORAGE_MASGIC_END != WIFI_MAGIC_END)
        {
            log_e("!!! WiFi Storage End Overflow !!!");
            return true;
        }
        if (wifi_stack->MASGIC_END != WIFI_MAGIC_END)
        {
            log_e("!!! WiFi End Overflow !!!");
            return true;
        }
    }
    return false;
}

uint8_t wifi_storage_init(void)
{
    if (wifi_stack && !wifi_stack->storage.init)
    {
        memset(&wifi_stack->storage, 0, sizeof(wifi_stack->storage));
        wifi_stack->storage.init = 1;
        if (config_get("WiFiStorage", (uint8_t *)&wifi_stack->storage, sizeof(wifi_stack->storage)))
        {
            wifi_stack->have_storage = 1;
#ifndef WIFI_STORAGE_ENABLE_SIMPLE
            log_i("wifi.storage: Find %d Save SSID", wifi_stack->storage.length);
#else
            if (wifi_stack->storage.infos[0].ssid[0])
                log_i("wifi.storage: Find SSID : %s", wifi_stack->storage.infos[0].ssid);
#endif
        }
        else
        {
            log_w("wifi.storage : Not find wifi_storage");
        }
    }
    return 0;
}

uint8_t wifi_storage_commit(void)
{
    if (!wifi_stack)
        return 0;
    if (config_set("WiFiStorage", sizeof(wifi_stack->storage), (uint8_t *)&wifi_stack->storage))
    {
        // elog_hexdump("", 16, &wifi_stack->storage, sizeof(wifi_stack->storage));
        return config_commit();
    }
    return 0;
}

#ifndef WIFI_STORAGE_ENABLE_SIMPLE
uint8_t wifi_storage_connect_state(void)
{
    if (!wifi_stack)
        return 0;
    uint8_t re = 0, save = 1;
    if (save)
    {
        log_i("[wifi.storage] 更新WiFi连接状态");
        return wifi_storage_commit();
    }
    return 0;
}
#endif

uint8_t wifi_storage_check(char *ssid)
{
    if (!wifi_stack || !ssid)
        return 0;
    uint8_t len = sizeof(wifi_stack->storage.infos) / sizeof(wifi_stack->storage.infos[0]);
    for (int8_t i = len - 1; i >= 0; i--)
    {
        if (!strncmp(wifi_stack->storage.infos[i].ssid, ssid, sizeof(wifi_stack->storage.infos[0].ssid)))
            return 1;
    }
    return 0;
}

#ifndef WIFI_STORAGE_ENABLE_SIMPLE
/* 将数据左移移 */
void wifi_storage_infos_move_left(wifi_storage_info_t infos, uint8_t index, uint8_t len)
{
    if (!infos)
        return;
    if (index >= len)
        index = 0;
    memmove(&infos[index], &infos[index + 1], sizeof(wifi_storage_info_s) * (len - index));
    memset(&infos[len - 1], 0, sizeof(wifi_storage_info_s));
}

/**
 *  淘汰太过久没有使用的 WiFi 记录, 将目前最新的保存下来
 */
void wifi_storage_infos_move(char *ssid, char *passwd, uint8_t target_index)
{
    if (!wifi_stack || !target_index || !ssid)
        return;
    /* This ssid and passwd Not in infos list */
    uint8_t len = sizeof(wifi_stack->storage.infos) / sizeof(wifi_stack->storage.infos[0]);
    wifi_storage_infos_move_left(wifi_stack->storage.infos, target_index, len);
    if (wifi_stack->storage.length)
    {
        if (wifi_stack->storage.length >= len)
            wifi_stack->storage.length--;
    }
    wifi_stack->storage.cur_connect = len - 1;
    if (ssid)
        strncpy(wifi_stack->storage.infos[wifi_stack->storage.cur_connect].ssid, ssid, sizeof(wifi_stack->storage.infos[wifi_stack->storage.cur_connect].ssid) - 1);
    if (passwd)
        strncpy(wifi_stack->storage.infos[wifi_stack->storage.cur_connect].passwd, passwd, sizeof(wifi_stack->storage.infos[wifi_stack->storage.cur_connect].passwd) - 1);
    if (target_index == 0xFF)
    {
        if (wifi_stack->storage.length < len)
        {
            len = wifi_stack->storage.length;
            wifi_stack->storage.length++;
        }
    }
}
#endif

uint8_t wifi_storage_save(char *ssid, char *passwd)
{
    if (!wifi_stack || !ssid)
    {
        log_e("wifi_stack(%p) ssid(%p)", wifi_stack, ssid);
        return 0;
    }
#ifndef WIFI_STORAGE_ENABLE_SIMPLE
    uint8_t save = 0xFF, index = 0xFF;
    uint8_t len = sizeof(wifi_stack->storage.infos) / sizeof(wifi_stack->storage.infos[0]);
    log_i("SSID %s PASSWORD %s", ssid, passwd);
    /* 查找相同SSID，并检查是否相同 */
    for (int8_t i = len - 1; i >= 0; i--)
    {
        if (!strncmp(ssid, wifi_stack->storage.infos[i].ssid, sizeof(wifi_stack->storage.infos[i].ssid)))
        {
            index = i;
            if (strncmp(passwd, wifi_stack->storage.infos[index].passwd, sizeof(wifi_stack->storage.infos[index].passwd)))
            {
                save = 1;
                log_i("wifi.save: 更新密码");
            }
            if (index != len - 1)
            {
                save = 1;
                log_i("wifi.save: 更新顺序");
            }
            break;
        }
    }
    // if (wifi_stack->save_connect_state)
    {
        if (!wifi_stack->storage.last_connect_state)
        {
            wifi_stack->storage.last_connect_state = 1;
            log_i("wifi.save: 更新连接状态");
        }
        else if (save == 0xFF && index != 0xFF)
            save = 0;
    }
    if (save || !wifi_stack->have_storage)
    {
        wifi_stack->have_storage = 1;
        /* 没有相同SSID */
        if (index != 0xFF && wifi_stack->storage.infos[0].ssid[0])
        {
            log_w("抛弃长时间没有连接过的 WiFi : %s", wifi_stack->storage.infos[0].ssid);
        }
        wifi_storage_infos_move(ssid, passwd, index);
        return wifi_storage_commit();
    }
    else
        log_i("wifi.save: 无需更新");
#else
    bool save = false;
    if (!strncmp(ssid, wifi_stack->storage.infos[0].ssid, sizeof(wifi_stack->storage.infos[0].ssid))) /* SSID 相同 */
    {
        if (strncmp(passwd, wifi_stack->storage.infos[0].passwd, sizeof(wifi_stack->storage.infos[0].passwd))) /* password 不同 */
        {
            save = true;
            log_i("wifi.save: 更新密码");
        }
    }
    else /* SSID 不同 */
        save = true;

    if (save || !wifi_stack->have_storage)
    {
        wifi_stack->have_storage = 1;
        return wifi_storage_commit();
    }
#endif
    return 0;
}

wifi_storage_info_t wifi_storage_get_last_connect()
{
#ifndef WIFI_STORAGE_ENABLE_SIMPLE
    if (!wifi_stack || wifi_stack->storage.cur_connect == -1)
    {
        if (wifi_stack)
            log_i("cur connect %d", wifi_stack->storage.cur_connect);
        else
            log_i("storage no init.");
        return 0;
    }
    return &wifi_stack->storage.infos[wifi_stack->storage.cur_connect];
#else
    if (wifi_stack->storage.infos[0].ssid[0]) /* 存在有效SSID */
    {
        log_i("last connect : %s", wifi_stack->storage.infos[0].ssid);
        return &wifi_stack->storage.infos[0];
    }
    return 0;
#endif
}

char *wifi_storage_get(char *ssid)
{
    if (!wifi_stack || !ssid)
        return 0;

    uint8_t len = sizeof(wifi_stack->storage.infos) / sizeof(wifi_stack->storage.infos[0]);
    for (int8_t i = len - 1; i >= 0; i--)
    {
        if (!strncmp(wifi_stack->storage.infos[i].ssid, ssid, sizeof(wifi_stack->storage.infos[0].ssid)))
            return wifi_stack->storage.infos[i].passwd;
    }

    return 0;
}

uint8_t wifi_storage_get_kes(char *buff)
{
    if (!wifi_stack || !buff)
    {
        log_e("wifi_storage no init.");
        return 0;
    }
#ifndef WIFI_STORAGE_ENABLE_SIMPLE
    log_i("save ssid %d", wifi_stack->storage.length);

    uint8_t len = sizeof(wifi_stack->storage.infos) / sizeof(wifi_stack->storage.infos[0]);
    for (int8_t i = len - 1; i >= 0; i--)
    {
        if (wifi_stack->storage.infos[i].ssid[0])
        {
            log_i("find ssid %s", wifi_stack->storage.infos[i].ssid);
            strcpy(buff, wifi_stack->storage.infos[i].ssid);
            buff += strlen(wifi_stack->storage.infos[i].ssid) + 1;
        }
    }
    buff[0] = '\0';
    return wifi_stack->storage.length;
#else
    if (!wifi_stack->storage.infos[0].ssid[0])
        return 0;

    log_i("find ssid %s", wifi_stack->storage.infos[0].ssid);
    strcpy(buff, wifi_stack->storage.infos[0].ssid);
    buff += strlen(wifi_stack->storage.infos[0].ssid) + 1;
    buff[0] = '\0';
    return 1;
#endif
}

uint8_t wifi_storage_delete(char *ssid)
{
    if (!wifi_stack || !ssid)
        return 0;
#ifndef WIFI_STORAGE_ENABLE_SIMPLE
    uint8_t change = 0;
    uint8_t len = sizeof(wifi_stack->storage.infos) / sizeof(wifi_stack->storage.infos[0]);
    for (int16_t i = len - 1; i >= 0; i--)
    {
        if (change)
        {
            if (wifi_stack->storage.infos[i].ssid[0])
            {
                change = 2;
                strncpy(wifi_stack->storage.infos[i + 1].ssid, wifi_stack->storage.infos[i].ssid, sizeof(wifi_stack->storage.infos[0].ssid));
                strncpy(wifi_stack->storage.infos[i + 1].passwd, wifi_stack->storage.infos[i].passwd, sizeof(wifi_stack->storage.infos[0].passwd));
            }
            else if (change == 2)
            {
                change = 1;
                memset(&wifi_stack->storage.infos[i + 1], 0, sizeof(wifi_stack->storage.infos[0]));
            }
            else
                break;
        }
        if (!strncmp(ssid, wifi_stack->storage.infos[i].ssid, sizeof(wifi_stack->storage.infos[i].ssid)))
        {
            printf("[wifi.storage.del] %s i:%d l:%d\n", ssid, i, len);
            wifi_storage_infos_move_left(wifi_stack->storage.infos, i, len);
            wifi_stack->storage.length--;
            if (!i)
            {
                memset(&wifi_stack->storage.infos[i], 0, sizeof(wifi_stack->storage.infos[0]));
                break;
            }
            change = 2;
        }
    }
#else
    if (!strncmp(ssid, wifi_stack->storage.infos[0].ssid, sizeof(wifi_stack->storage.infos[0].ssid)))
    {
        memset(&wifi_stack->storage.infos[0], 0, sizeof(wifi_stack->storage.infos[0]));
        return wifi_storage_commit();
    }
#endif
    return 0;
}

__attribute__((weak)) void wifi_status_notice_cb(uint8_t status, void *value)
{
    if (status > WIFI_STATUS_MAX)
        status = WIFI_STATUS_MAX;
    log_i("default wifi_status_notice_cb : %s %p", WifiStatusType2Str[status], value);
}

uint8_t wifi_scan_start(uint8_t block)
{
    if (!wifi_stack)
        return -1;
    wifi_stack->scan_ap_count = 0;
    wifi_stack->scan = 1;
    if (wifi_stack->start)
        return esp_wifi_scan_start(NULL, block ? true : false) == ESP_OK ? 1 : 0;
    else
        return 1;
}

uint16_t wifi_get_scan_ap_num(void)
{
    if (!wifi_stack)
        return 0;
    return wifi_stack->scan_ap_count;
}

wifi_ap_record_t *scan_ap_info(void)
{
    if (!wifi_stack)
        return 0;
    return wifi_stack->scan_ap_infos;
}

char *wifi_get_sta_ssid(void)
{
    if (!wifi_stack)
        return "NULL";
    return wifi_stack->sta_ssid;
}

char *wifi_get_ap_ssid(void)
{
    if (!wifi_stack)
        return "NULL";
    return (char *)wifi_stack->config.ap.ssid;
}

char *wifi_get_ap_passwd(void)
{
    if (!wifi_stack)
        return "NULL";
    return (char *)wifi_stack->config.ap.password;
}

int8_t wifi_get_scan_ap_info_to(char *to, uint16_t size)
{
    if (!wifi_stack || !wifi_stack->scan_ap_count)
        return 0;
    if (!to || size < 3)
        return -1;
    char *p = to;
    *p++ = '[';
    for (uint16_t i = 0; i < wifi_stack->scan_ap_count; i++)
    {
        if (size - (p - to) < 38 + strlen((char *)wifi_stack->scan_ap_infos[i].ssid))
            break;
        p += snprintf(p, size - (p - to), "{\"ssid\":\"%s\",", (char *)wifi_stack->scan_ap_infos[i].ssid);
        p += snprintf(p, size - (p - to), "\"rssi\":\"%d\",", wifi_stack->scan_ap_infos[i].rssi);
        p += snprintf(p, size - (p - to), "\"channel\":\"%d\"}", wifi_stack->scan_ap_infos[i].primary);
        if (i + 1 < wifi_stack->scan_ap_count)
            *p++ = ',';
    }
    *p++ = ']';
    *p++ = '\0';
    return 0;
}

static const char *get_wifi_event_str(int32_t event_id)
{
#define case_return_str(EVENT) \
    case EVENT:                \
        return #EVENT
    switch (event_id)
    {
        case_return_str(WIFI_EVENT_WIFI_READY);
        case_return_str(WIFI_EVENT_SCAN_DONE);
        case_return_str(WIFI_EVENT_STA_START);
        case_return_str(WIFI_EVENT_STA_STOP);
        case_return_str(WIFI_EVENT_STA_CONNECTED);
        case_return_str(WIFI_EVENT_STA_DISCONNECTED);
        case_return_str(WIFI_EVENT_STA_AUTHMODE_CHANGE);
        case_return_str(WIFI_EVENT_STA_WPS_ER_SUCCESS);
        case_return_str(WIFI_EVENT_STA_WPS_ER_FAILED);
        case_return_str(WIFI_EVENT_STA_WPS_ER_TIMEOUT);
        case_return_str(WIFI_EVENT_STA_WPS_ER_PIN);
        case_return_str(WIFI_EVENT_STA_WPS_ER_PBC_OVERLAP);
        case_return_str(WIFI_EVENT_AP_START);
        case_return_str(WIFI_EVENT_AP_STOP);
        case_return_str(WIFI_EVENT_AP_STACONNECTED);
        case_return_str(WIFI_EVENT_AP_STADISCONNECTED);
        case_return_str(WIFI_EVENT_AP_PROBEREQRECVED);
        case_return_str(WIFI_EVENT_FTM_REPORT);
        case_return_str(WIFI_EVENT_STA_BSS_RSSI_LOW);
        case_return_str(WIFI_EVENT_ACTION_TX_STATUS);
        case_return_str(WIFI_EVENT_ROC_DONE);
        case_return_str(WIFI_EVENT_STA_BEACON_TIMEOUT);
        case_return_str(WIFI_EVENT_CONNECTIONLESS_MODULE_WAKE_INTERVAL_START);
        case_return_str(WIFI_EVENT_AP_WPS_RG_SUCCESS);
        case_return_str(WIFI_EVENT_AP_WPS_RG_FAILED);
        case_return_str(WIFI_EVENT_AP_WPS_RG_TIMEOUT);
        case_return_str(WIFI_EVENT_AP_WPS_RG_PIN);
        case_return_str(WIFI_EVENT_AP_WPS_RG_PBC_OVERLAP);
        case_return_str(WIFI_EVENT_ITWT_SETUP);
        case_return_str(WIFI_EVENT_ITWT_TEARDOWN);
        case_return_str(WIFI_EVENT_ITWT_PROBE);
        case_return_str(WIFI_EVENT_ITWT_SUSPEND);
        case_return_str(WIFI_EVENT_NAN_STARTED);
        case_return_str(WIFI_EVENT_NAN_STOPPED);
        case_return_str(WIFI_EVENT_NAN_SVC_MATCH);
        case_return_str(WIFI_EVENT_NAN_REPLIED);
        case_return_str(WIFI_EVENT_NAN_RECEIVE);
        case_return_str(WIFI_EVENT_NDP_INDICATION);
        case_return_str(WIFI_EVENT_NDP_CONFIRM);
        case_return_str(WIFI_EVENT_NDP_TERMINATED);
        case_return_str(WIFI_EVENT_HOME_CHANNEL_CHANGE);
    default:
        return "Undefined";
    }
    return "Undefined";
}

void event_handler(void *arg, esp_event_base_t event_base,
                   int32_t event_id, void *event_data)
{
    log_i("event handler %s %s(%ld)", event_base, get_wifi_event_str(event_id), event_id);

    if (!wifi_stack)
        return;
    wifi_stack->in_event_cb = 1;
    if (event_base == WIFI_EVENT)
    {
        if (event_id == WIFI_EVENT_STA_START)
        {
            wifi_stack->start = 1;
            if (wifi_stack->connecting)
                esp_wifi_connect();
            if (wifi_stack->notice_cb)
                wifi_stack->notice_cb(WIFI_STATUS_STA_START, (void *)1);
            if (wifi_stack->scan)
                esp_wifi_scan_start(NULL, false);
        }
        else if (event_id == WIFI_EVENT_STA_STOP)
        {
            wifi_stack->start = 0;
            wifi_stack->connect = 0;
            if (wifi_stack->notice_cb)
                wifi_stack->notice_cb(WIFI_STATUS_STA_STOP, (void *)1);
        }
        else if (event_id == WIFI_EVENT_STA_CONNECTED)
        {
            log_i("wifi.event: connected, waitting get ip");
            if (wifi_stack->notice_cb)
                wifi_stack->notice_cb(WIFI_STATUS_STA_CONNECTED, wifi_stack->config.sta.ssid);
        }
        else if (event_id == WIFI_EVENT_STA_DISCONNECTED)
        {
            log_w("wifi.event: disconnected");
            uint8_t connect = wifi_stack->connect;
            uint8_t user_call = wifi_stack->user_call_disconnect || wifi_stack->user_call_connect;
            wifi_disconnect();
            if (wifi_stack->s_retry_num)
            {
            retry_connect:
                // if (wifi_stack->scan)
                //     esp_wifi_scan_stop();
                esp_wifi_connect();
                if (wifi_stack->notice_cb)
                    wifi_stack->notice_cb(WIFI_STATUS_STA_CONNECTING, wifi_stack->config.sta.ssid);
                wifi_stack->s_retry_num--;
                log_i("retry to connect to the AP");
            }
            else if (connect && !user_call) /* 意外断开- 尝试5次重连 */
            {
                wifi_stack->s_retry_num = 5;
                goto retry_connect;
            }
            else if (wifi_stack->storage.mode == WIFI_MODE_AUTO && !(wifi_stack->mode & WIFI_MODE_AP))
            {
                wifi_start_ap();
                wifi_set_ap(wifi_stack->default_ap_ssid, DEFAULT_WIFI_AP_PASSWD);
            }
        }
        else if (event_id == WIFI_EVENT_AP_STACONNECTED)
        {
            wifi_event_ap_staconnected_t *event = (wifi_event_ap_staconnected_t *)event_data;
            log_i("wifi.event : station " MACSTR " join, AID=%d",
                  MAC2STR(event->mac), event->aid);
            if (wifi_stack->notice_cb)
                wifi_stack->notice_cb(WIFI_STATUS_AP_CONNECTED, event->mac);
        }
        else if (event_id == WIFI_EVENT_AP_STADISCONNECTED)
        {
            wifi_event_ap_stadisconnected_t *event = (wifi_event_ap_stadisconnected_t *)event_data;
            log_i("wifi.event : station " MACSTR " leave, AID=%d",
                  MAC2STR(event->mac), event->aid);
            if (wifi_stack->notice_cb)
                wifi_stack->notice_cb(WIFI_STATUS_AP_DISCONNECTED, event->mac);
        }
        else if (event_id == WIFI_EVENT_SCAN_DONE)
        {
            esp_err_t err = esp_wifi_scan_get_ap_num(&wifi_stack->scan_ap_count);
            memset(wifi_stack->scan_ap_infos, 0, sizeof(wifi_stack->scan_ap_infos));
            memset(wifi_stack->aps_ssid, 0, sizeof(wifi_stack->aps_ssid));
            log_i("wifi.event : aps count %d", wifi_stack->scan_ap_count);
            if (wifi_stack->scan_ap_count)
            {
                if (wifi_stack->scan_ap_count > CONFIG_WIFI_PROV_SCAN_MAX_ENTRIES)
                    wifi_stack->scan_ap_count = CONFIG_WIFI_PROV_SCAN_MAX_ENTRIES;
                esp_wifi_scan_get_ap_records(&wifi_stack->scan_ap_count, wifi_stack->scan_ap_infos);
                for (uint8_t i = 0; i < wifi_stack->scan_ap_count; i++)
                {
                    wifi_stack->aps_ssid[i] = (char *)wifi_stack->scan_ap_infos[i].ssid;
                    wifi_stack->aps_rssi[i] = wifi_stack->scan_ap_infos[i].rssi;
                }
            }

            if (wifi_stack->notice_cb)
            {
                if (!wifi_stack->got_ip)
                    wifi_stack->notice_cb(WIFI_STATUS_STA_SSID, 0);
                if (wifi_stack->scan_ap_count)
                    wifi_stack->notice_cb(WIFI_STATUS_SCAN_DONE, wifi_stack->aps_ssid);
                else
                    wifi_stack->notice_cb(WIFI_STATUS_SCAN_DONE, 0);
            }

            if (!wifi_stack->user_call_scan)
            {
                log_i("wifi.event: Total APs scanned = %u", wifi_stack->scan_ap_count);
                for (int i = 0; i < wifi_stack->scan_ap_count; i++)
                {
                    log_i("SSID \t\t%s", wifi_stack->scan_ap_infos[i].ssid);
                    log_i("RSSI \t\t%d", wifi_stack->scan_ap_infos[i].rssi);
                    // print_auth_mode(wifi_stack->scan_ap_info[i].authmode);
                    // if (wifi_stack->scan_ap_info[i].authmode != WIFI_AUTH_WEP) {
                    //     print_cipher_type(wifi_stack->scan_ap_info[i].pairwise_cipher, wifi_stack->scan_ap_info[i].group_cipher);
                    // }
                    log_i("Channel \t\t%d", wifi_stack->scan_ap_infos[i].primary);
                }
            }

            if (wifi_stack->user_call_scan)
            {
                if (!wifi_stack->connect)
                {
                    if (wifi_stack->storage.mode == WIFI_MODE_USER_CONFIG)
                        wifi_del();
                    else
                        wifi_stop_sta();
                }
                wifi_stack->user_call_scan = 0;
            }
            wifi_stack->scan = 0;
            log_i("WiFiEventScanDoneEnd");
        }
    }
    else if (event_base == SC_EVENT)
    {
        if (event_id == SC_EVENT_SCAN_DONE)
            log_i("Scan done");
        else if (event_id == SC_EVENT_FOUND_CHANNEL)
            log_i("Found channel");
        else if (event_id == SC_EVENT_GOT_SSID_PSWD)
        {
            log_i("Got SSID and password");
            smartconfig_event_got_ssid_pswd_t *evt = (smartconfig_event_got_ssid_pswd_t *)event_data;
            wifi_set_sta((char *)evt->ssid, (char *)evt->password);
        }
        else if (event_id == SC_EVENT_GOT_SSID_PSWD)
        {
            log_i("smartconfig over");
            esp_smartconfig_stop();
        }
    }
    else if (event_base == IP_EVENT)
    {
        if (event_id == IP_EVENT_STA_GOT_IP)
        {
            wifi_stack->got_ip = 1;
            ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;

            strcpy(wifi_stack->sta_ssid, (char *)wifi_stack->config.sta.ssid);
            wifi_storage_save((char *)wifi_stack->config.sta.ssid, (char *)wifi_stack->config.sta.password);
            wifi_stack->s_retry_num = 0;
            wifi_stack->connecting = 0;
            wifi_stack->connect = 1;

            memcpy(&wifi_stack->ip_info, &event->ip_info, sizeof(esp_netif_ip_info_t));
            snprintf(wifi_stack->ip, 16, IPSTR, IP2STR(&wifi_stack->ip_info.ip));
            snprintf(wifi_stack->netmask, 16, IPSTR, IP2STR(&wifi_stack->ip_info.netmask));
            snprintf(wifi_stack->gw, 16, IPSTR, IP2STR(&wifi_stack->ip_info.gw));

            log_i("[hal.wifi] Got ip : " IPSTR, IP2STR(&event->ip_info.ip));
            wifi_network_status_cb(true);
            if (wifi_stack->notice_cb)
            {
                wifi_stack->notice_cb(WIFI_STATUS_STA_SSID, wifi_stack->sta_ssid);
                wifi_stack->notice_cb(WIFI_STATUS_GOT_IP, wifi_stack->ip);
                wifi_stack->notice_cb(WIFI_STATUS_STA_CONNECT_RESULT, 0);
            }
            // #ifdef CONFIG_DRIVER_USE_NTP
            extern void sntp_start(void);
            sntp_start();
            // #endif

            extern void bemfa_server_start(void);
            bemfa_server_start();
            extern void mqtt_server_start(void);
            mqtt_server_start();
            if (!wifi_stack->task_up)
            {
                wifi_stack->task_up = 1;
                extern void http_server_start(char *base_path);
                http_server_start("/www");
                udp.multicast("238.84.84.71", 8471); /* 238.T.T.G:TG */
            }
            if (wifi_stack->storage.mode == WIFI_MODE_AUTO)
            {
                if (wifi_stack->mode & WIFI_MODE_AP)
                {
                    esp_wifi_set_mode(WIFI_MODE_STA);
                    wifi_stack->mode &= ~WIFI_MODE_AP;
                    log_w("Mode AUTO , Close Soft AP");
                }
            }
            else if (wifi_stack->storage.mode == WIFI_MODE_AP_STA || wifi_stack->storage.mode == WIFI_MODE_USER_CONFIG)
            {
                if (wifi_stack->mode & WIFI_MODE_AP)
                    wifi_set_ap_dns(wifi_stack->gw);
            }
        }
        else if (event_id == IP_EVENT_STA_LOST_IP)
        {
            wifi_stack->got_ip = 0;
            wifi_stack->s_retry_num = ESP_MAXIMUM_RETRY;
            wifi_stack->connect = 0;
            log_i("sta lose ip.");
            wifi_network_status_cb(false);
            if (wifi_stack->connecting || wifi_stack->scan)
                return;
            memset(&wifi_stack->ip_info, 0, sizeof(esp_netif_ip_info_t));
            memset(wifi_stack->sta_ssid, 0, 33);
            if (wifi_stack->notice_cb)
            {
                wifi_stack->notice_cb(WIFI_STATUS_STA_SSID, 0);
                wifi_stack->notice_cb(WIFI_STATUS_LOST_IP, 0);
            }
            // if (wifi_stack->user_call_scan)
            // {
            //     if (wifi_stack->notice_cb)
            //         wifi_stack->notice_cb(WIFI_STATUS_STA_CONNECT_RESULT, -1);
            //     wifi_del();
            //     wifi_stack->user_call_scan = 0;
            // }
            if (wifi_stack->storage.mode == WIFI_MODE_AUTO)
            {
                if (!(wifi_stack->mode & WIFI_MODE_AP))
                {
                    esp_wifi_set_mode(WIFI_MODE_AP);
                    wifi_stack->mode = WIFI_MODE_AP;
                    log_w("Mode AUTO , Enable Soft AP");
                }
            }
            else if (wifi_stack->storage.mode != WIFI_MODE_AP_STA)
                wifi_del();
        }
        else if (event_id == IP_EVENT_AP_STAIPASSIGNED)
        {
            ip_event_ap_staipassigned_t *event = (ip_event_ap_staipassigned_t *)event_data;
            log_i("ap got ip:" IPSTR, IP2STR(&event->ip));
            if (!wifi_stack->task_up)
            {
                wifi_stack->task_up = 1;
                extern void http_server_start(char *base_path);
                http_server_start("/www");
                udp.multicast("238.84.84.71", 8471); /* 238.T.T.G:TG */
            }
            if (wifi_stack->notice_cb)
                wifi_stack->notice_cb(WIFI_STATUS_AP_GOT_IP, (void *)event->ip.addr);
        }
    }
    wifi_stack->in_event_cb = 0;
}

void wifi_set_ap(char *ssid, char *passwd)
{
    if (!wifi_stack)
        return;

    if (ssid)
    {
        memset(wifi_stack->config.ap.ssid, 0, sizeof(wifi_stack->config.ap.ssid));
        memcpy(wifi_stack->config.ap.ssid, ssid, strlen(ssid));
        wifi_stack->config.ap.ssid_len = strlen(ssid);
    }

    memset(wifi_stack->config.ap.password, 0, sizeof(wifi_stack->config.ap.password));
    if (passwd)
    {
        memcpy(wifi_stack->config.ap.password, passwd, strlen(passwd));
        wifi_stack->config.ap.authmode = WIFI_AUTH_WPA_WPA2_PSK;
    }
    else
        wifi_stack->config.ap.authmode = WIFI_AUTH_OPEN;

    if (ssid || passwd)
    {
        log_i("AP : %s %s", wifi_stack->config.ap.ssid, wifi_stack->config.ap.password);
        esp_wifi_set_config(WIFI_IF_AP, &wifi_stack->config);

        if (wifi_stack->got_ip)
            wifi_set_ap_dns(wifi_stack->gw);
    }
}

void wifi_set_sta(char *ssid, char *passwd)
{
    if (!wifi_stack)
    {
        log_e("wifi no init.");
        return;
    }
    if (ssid)
    {
        memset(wifi_stack->config.sta.ssid, 0, sizeof(wifi_stack->config.sta.ssid));
        memcpy(wifi_stack->config.sta.ssid, ssid, strlen(ssid));
    }
    if (passwd)
    {
        memset(wifi_stack->config.sta.password, 0, sizeof(wifi_stack->config.sta.password));
        memcpy(wifi_stack->config.sta.password, passwd, strlen(passwd));
        wifi_stack->config.sta.threshold.authmode = WIFI_AUTH_WPA_WPA2_PSK;
    }
    else
        wifi_stack->config.sta.threshold.authmode = WIFI_AUTH_OPEN;
    if (ssid || passwd)
    {
        esp_wifi_disconnect();
        wifi_stack->connect = 0;
        esp_err_t err = esp_wifi_set_config(WIFI_IF_STA, &wifi_stack->config);
        if (err == ESP_OK)
        {
            log_i("STA : %s %s", wifi_stack->config.sta.ssid, wifi_stack->config.sta.password);
            wifi_stack->connecting = 1;
            wifi_stack->s_retry_num = ESP_MAXIMUM_RETRY;
            if (wifi_stack->start)
                esp_wifi_connect();
        }
        else
            log_e("Failed to config wifi sta infos %s", esp_err_to_name(err));
    }
}

void wifi_set_sta_ip(char *ip, char *mask, char *gw)
{
    if (!wifi_stack)
        return;
    esp_netif_dhcpc_stop(wifi_stack->sta_netif);
    ip4addr_aton(ip, (ip4_addr_t *)&wifi_stack->storage.sta_ip_config.ip);
    ip4addr_aton(mask, (ip4_addr_t *)&wifi_stack->storage.sta_ip_config.mask);
    ip4addr_aton(gw, (ip4_addr_t *)&wifi_stack->storage.sta_ip_config.gateway);
    esp_netif_set_ip_info(wifi_stack->sta_netif, (const esp_netif_ip_info_t *)&wifi_stack->storage.sta_ip_config);
}

void wifi_set_ap_ip(char *ip, char *mask, char *gw)
{
    if (!wifi_stack)
        return;
    esp_netif_dhcpc_stop(wifi_stack->ap_netif);
    ip4addr_aton(ip, (ip4_addr_t *)&wifi_stack->storage.ap_ip_config.ip);
    ip4addr_aton(mask, (ip4_addr_t *)&wifi_stack->storage.ap_ip_config.mask);
    ip4addr_aton(gw, (ip4_addr_t *)&wifi_stack->storage.ap_ip_config.gateway);
    esp_netif_set_ip_info(wifi_stack->ap_netif, (const esp_netif_ip_info_t *)&wifi_stack->storage.ap_ip_config);
}

void wifi_set_ap_dns(char *dns_str)
{
    ip4_addr_t ip;
    ip4addr_aton(dns_str, &ip);
    esp_netif_dns_info_t dns;
    dns.ip.u_addr.ip4.addr = ip.addr;
    dns.ip.type = IPADDR_TYPE_V4;
    dhcps_offer_t dhcps_dns_value = OFFER_DNS;
    esp_netif_dhcps_stop(wifi_stack->ap_netif);
    esp_netif_dhcps_option(wifi_stack->ap_netif, ESP_NETIF_OP_SET, ESP_NETIF_DOMAIN_NAME_SERVER, &dhcps_dns_value, sizeof(dhcps_dns_value));
    esp_netif_set_dns_info(wifi_stack->ap_netif, ESP_NETIF_DNS_MAIN, &dns);
    esp_netif_dhcps_start(wifi_stack->ap_netif);
}

void wifi_start_sta()
{
    if (!wifi_stack)
    {
        log_e("wifi no init.");
        return;
    }

    if (wifi_stack->mode & WIFI_MODE_AP)
    {
        if (esp_wifi_set_mode(WIFI_MODE_APSTA) == ESP_OK)
            wifi_stack->mode = WIFI_MODE_APSTA;
    }
    else if (!(wifi_stack->mode & WIFI_MODE_STA))
    {
        if (esp_wifi_set_mode(WIFI_MODE_STA) == ESP_OK)
            wifi_stack->mode = WIFI_MODE_STA;
    }
}

void wifi_start_ap()
{
    if (!wifi_stack)
        return;
    if (wifi_stack->mode & WIFI_MODE_STA)
    {
        esp_wifi_set_mode(WIFI_MODE_APSTA);
        wifi_stack->mode = WIFI_MODE_APSTA;
    }
    else
    {
        esp_wifi_set_mode(WIFI_MODE_AP);
        wifi_stack->mode = WIFI_MODE_AP;
    }
}

void wifi_start_smartconfig(uint8_t type)
{
    if (!wifi_stack)
        return;
    if (type > SC_TYPE_ESPTOUCH_V2)
        return;
    if (wifi_stack->mode & WIFI_MODE_STA)
    {
        esp_err_t err = esp_smartconfig_set_type((smartconfig_type_t)type);
        if (err == ESP_OK)
        {
            smartconfig_start_config_t cfg = SMARTCONFIG_START_CONFIG_DEFAULT();
            err = esp_smartconfig_start(&cfg);
            if (err == ESP_OK)
                log_i("Smartconfig started");
            else
                log_e("Smartconfig start failed %s", esp_err_to_name(err));
        }
        else
            log_e("Smartconfig set type failed %s", esp_err_to_name(err));
    }
}

void wifi_stop_sta()
{
    if (!wifi_stack)
        return;
    if (wifi_stack->mode & WIFI_MODE_STA)
    {
        if (wifi_stack->mode == WIFI_MODE_APSTA)
        {
            esp_wifi_set_mode(WIFI_MODE_AP);
            wifi_stack->mode = WIFI_MODE_AP;
        }
        else
        {
            esp_wifi_set_mode(WIFI_MODE_NULL);
            wifi_stack->mode = WIFI_MODE_NULL;
        }
    }
}

void wifi_stop_ap()
{
    if (!wifi_stack)
        return;
    if (wifi_stack->mode & WIFI_MODE_AP)
    {
        if (wifi_stack->mode == WIFI_MODE_APSTA)
        {
            esp_wifi_set_mode(WIFI_MODE_STA);
            wifi_stack->mode = WIFI_MODE_STA;
        }
        else
        {
            esp_wifi_set_mode(WIFI_MODE_NULL);
            wifi_stack->mode = WIFI_MODE_NULL;
        }
    }
}

void wifi_start(void)
{
    if (!wifi_stack || wifi_stack->start)
        return;
    log_i("Start.");
    if (esp_wifi_start() == ESP_OK)
    {
        if (wifi_stack->notice_cb)
            wifi_stack->notice_cb(WIFI_STATUS_START, (void *)1);
    }
}

void wifi_stop(void)
{
    if (!wifi_stack)
        return;
    if (wifi_stack->start)
    {
        esp_wifi_stop();
        if (wifi_stack->notice_cb)
            wifi_stack->notice_cb(WIFI_STATUS_STOP, (void *)1);
    }
}

void wifi_disconnect(void)
{
    if (!wifi_stack || wifi_stack->in_disconnect)
        return;
    wifi_stack->in_disconnect = 1;
    if (!wifi_stack->s_retry_num)
    {
        wifi_stack->connecting = 0;
        if (wifi_stack->user_call_disconnect)
        {
            wifi_stack->user_call_disconnect = 0;
            wifi_del();
        }
        else
        {
            if (wifi_stack->user_call_connect)
            {
                wifi_stack->user_call_connect = 0;
                wifi_del();
                if (wifi_stack->notice_cb)
                    wifi_stack->notice_cb(WIFI_STATUS_STA_CONNECT_RESULT, (void *)-1);
            }
        }
    }

    if (wifi_stack->connect)
    {
#ifndef WIFI_STORAGE_ENABLE_SIMPLE
        if (wifi_stack->storage.last_connect_state)
        {
            wifi_stack->storage.last_connect_state = 0;
            wifi_storage_connect_state();
        }
#endif
        memset(wifi_stack->sta_ssid, 0, 33);
        if (!wifi_stack->in_event_cb)
            wifi_stack->s_retry_num = 0;
        if (wifi_stack->notice_cb)
            wifi_stack->notice_cb(WIFI_STATUS_STA_DISCONNECTED, wifi_stack->sta_ssid);
    }
    esp_wifi_disconnect();
    wifi_stack->connect = 0;
    wifi_stack->in_disconnect = 0;
}

void wifi_del()
{
    if (!wifi_stack || wifi_stack->in_del)
        return;
    wifi_stack->in_del = 1;
    wifi_stack->s_retry_num = ESP_MAXIMUM_RETRY;
    wifi_disconnect();
    wifi_stop();
    uint8_t init = wifi_stack->init;
    // if (wifi_stack->init)
    // {
    //     if (wifi_stack->notice_cb)
    //         wifi_stack->notice_cb(WIFI_STATUS_DEINIT, 1);
    //     esp_wifi_deinit();
    // }
    // memset(&wifi_stack->config, 0, sizeof(wifi_stack_s) - sizeof(void *) * 4 - sizeof(wifi_storage_s) - sizeof(wifi_stack->MASGIC_END) * 2);
    memset(&wifi_stack->config, 0, (uint32_t)&wifi_stack->MASGIC_END - (uint32_t)&wifi_stack->config);
    wifi_stack->init = init;
    log_i("wifi deinit");
}

void wifi_init(void)
{
    if (!wifi_stack)
    {
        /* 初始化WIFI */
        log_i("init start");
        wifi_stack = _malloc_(sizeof(wifi_stack_s));
        if (!wifi_stack)
        {
            log_e("Failed to malloc wifi_stack_s\n");
            return;
        }
        memset(wifi_stack, 0, sizeof(wifi_stack_s));
        wifi_stack->MASGIC_START = WIFI_MAGIC_START;
        wifi_stack->MASGIC_END = WIFI_MAGIC_END;
        wifi_stack->STORAGE_MASGIC_START = WIFI_MAGIC_START;
        wifi_stack->STORAGE_MASGIC_END = WIFI_MAGIC_END;
        wifi_stack->config.sta.pmf_cfg.capable = true;
        wifi_stack->config.sta.pmf_cfg.required = false;
        wifi_stack->config.ap.channel = 6;
        wifi_stack->config.ap.max_connection = 4;
        wifi_stack->notice_cb = wifi_status_notice_cb;
        /* WIFI数据存储初始化 */
        wifi_storage_init();
        if (wifi_stack->storage.ap_ip_config.ip == 0 || wifi_stack->storage.ap_ip_config.ip == 0xffffffff)
        {
            ip4addr_aton("192.168.4.1", (ip4_addr_t *)&wifi_stack->storage.ap_ip_config.ip);
            ip4addr_aton("255.255.255.0", (ip4_addr_t *)&wifi_stack->storage.ap_ip_config.mask);
            ip4addr_aton("192.168.4.1", (ip4_addr_t *)&wifi_stack->storage.ap_ip_config.gateway);
        }
        wifi_stack->ap_netif = esp_netif_create_default_wifi_ap();
        wifi_stack->sta_netif = esp_netif_create_default_wifi_sta();
        /* 2023/11/15添加hostname */
        uint8_t mac[6] = {0};
        esp_app_desc_t run_app_info;
        char host_name[64] = {0};
        const esp_partition_t *run_app_partition = esp_ota_get_running_partition();
        esp_base_mac_addr_get(mac);
        esp_ota_get_partition_description(run_app_partition, &run_app_info);
        snprintf(host_name, sizeof(host_name), "%s-%02x%02x%02x%02x", run_app_info.project_name, mac[2], mac[3], mac[4], mac[5]);
        snprintf(wifi_stack->default_ap_ssid, sizeof(wifi_stack->default_ap_ssid) - 1, DEFAULT_WIFI_AP_SSID "-%02x%02x%02x%02x", mac[2], mac[3], mac[4], mac[5]);
        esp_netif_set_hostname(wifi_stack->sta_netif, host_name);

        ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
                                                            ESP_EVENT_ANY_ID,
                                                            &event_handler,
                                                            NULL,
                                                            &wifi_stack->instance_any_id));
        ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
                                                            ESP_EVENT_ANY_ID,
                                                            &event_handler,
                                                            NULL,
                                                            &wifi_stack->instance_got_ip));
        ESP_ERROR_CHECK(esp_event_handler_register(SC_EVENT,
                                                   ESP_EVENT_ANY_ID,
                                                   &event_handler,
                                                   NULL));
    }
    if (wifi_stack)
    {
        if (!wifi_stack->init)
        {
            wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
            if (esp_wifi_init(&cfg) == ESP_OK)
            {
                wifi_stack->init = 1;
                esp_wifi_set_storage(WIFI_STORAGE_RAM);
                // esp_wifi_set_ps(WIFI_PS_MAX_MODEM);
                if (wifi_stack->notice_cb)
                    wifi_stack->notice_cb(WIFI_STATUS_INIT, (void *)1);
            }
            else
                log_e("Failed to init wifi %s", esp_err_to_name(esp_wifi_init(&cfg)));
        }
        if (wifi_stack->init)
        {
            wifi_start();
#ifdef CONFIG_EXAMPLE_WIFI_SSID
            wifi_start_sta();
            wifi_set_sta(CONFIG_EXAMPLE_WIFI_SSID, CONFIG_EXAMPLE_WIFI_PASSWORD);
#endif
            if (wifi_stack->storage.mode != WIFI_MODE_ONLY_AP)
            {
                wifi_storage_info_t info = wifi_storage_get_last_connect();
                if (info && strnlen(info->ssid, sizeof(info->ssid)))
                {
                    log_i("Got last connect info : %s %s", info->ssid, info->passwd ? info->passwd : "");
                    wifi_start_sta();
                    wifi_set_sta(info->ssid, info->passwd);
                }
            }

            if (
                wifi_stack->storage.mode == WIFI_MODE_ONLY_AP ||
                wifi_stack->storage.mode == WIFI_MODE_AP_STA ||
                (wifi_stack->storage.mode == WIFI_MODE_AUTO && !(wifi_stack->mode & WIFI_MODE_STA)))
            {
                wifi_start_ap();
                wifi_set_ap(wifi_stack->default_ap_ssid, DEFAULT_WIFI_AP_PASSWD);
            }
        }
    }
    log_i("init finished");
}

uint32_t wifi_get_localIP()
{
    if (!wifi_stack)
        return 0;
    return wifi_stack->ip_info.ip.addr;
}
uint32_t wifi_get_subnetMask()
{
    if (!wifi_stack)
        return 0;
    return wifi_stack->ip_info.netmask.addr;
}

uint32_t wifi_get_gateway()
{
    if (!wifi_stack)
        return 0;
    return wifi_stack->ip_info.gw.addr;
}

void wifi_bind_notice_cb(wifi_status_notice_cb_t cb)
{
    if (wifi_stack)
        wifi_stack->notice_cb = cb;
}

__attribute__((weak)) void wifi_network_status_cb(bool status)
{
}
